package sma.organizations.roles;

import javax.vecmath.Vector2d;

import org.janusproject.kernel.agentsignal.QueuedSignalAdapter;
import org.janusproject.kernel.crio.core.Role;
import org.janusproject.kernel.message.Message;
import org.janusproject.kernel.status.Status;
import org.janusproject.kernel.status.StatusFactory;

import appli.utils.Config;

import sma.agents.bodies.CircleBody;
import sma.organizations.messages.RepulsiveMessage;
import sma.organizations.signals.SendCreateObstacleSignal;
import sma.organizations.signals.SendDeleteRepulsiveSignal;
import sma.organizations.signals.SendMoveObstacleSignal;
import sma.organizations.signals.SendMoveSignal;

/**
 * 
 * @author Etienne Sainton
 * @version 0.3
 * @since 0.1
 *
 */
public class Obstacle extends Role {

	private State currentState = State.I_M_HERE;

	private Integer currentPosition = 8000;

	private Integer id;
	
	private Integer groupe;

	private CircleBody body;
	
	boolean polaire;
	
	//Config.LIMIT limit;
	private boolean limit;
	
	// passage de l'angle en radians
	private double angle;

	// mise l'chelle de la fentre
	private double dist;

	private Vector2d coord = new Vector2d();
	
	/**
	 * La rotation selon l'angle du télémètre
	 */
	private int rotation;
	
	/**
	 * Create a local listener on the signals coming from the agent itself.
	 */
	private QueuedSignalAdapter<SendMoveObstacleSignal> privateMoveSignals = new QueuedSignalAdapter<SendMoveObstacleSignal>(SendMoveObstacleSignal.class);

	private int lastObstaclePosition;

	/**
	 * Cette fonction initialise le rôle obstacle 
	 * @param _id identifiant de l'agent
	 * @param _body sa représentation (généralement un ObstacleBody)
	 * @param _polaire est ce qu'il est dans une système à coordonnées polaires
	 * @param _groupe à quel groupe appartient il ?
	 */
	private void init(Integer _id, CircleBody _body, boolean _polaire, int _groupe, int _rotation) {
		this.id = _id;
		this.body = _body;
		this.polaire = _polaire;
		this.groupe = _groupe;
		this.rotation = _rotation;

		//this.limit = Config.LIMIT.NONE;
		this.limit = false;
		this.lastObstaclePosition = -1;
		if(polaire)
			angle = (this.id * Math.PI) / 180;
	}

	@Override
	public Status activate(Object... params) {
		init((Integer) params[0], (CircleBody) params[1], (Boolean) params[2], (Integer) params[3], (Integer) params[4]);
		
		getSignalEmitter().addSignalListener(this.privateMoveSignals);

		if (Config.DEBUG == true)
			System.out.println("Le role Obstacle est initialisé");

		return StatusFactory.ok(this);
	}

	@Override
	public Status live() {
		this.currentState = run();
		return StatusFactory.ok(this);
	}

	private State run() {
		switch (this.currentState) {
			case I_M_HERE:
				// J'envoie ma position à tout le monde :D
				broadcastMessage(Decisive.class, new RepulsiveMessage(this.id, this.groupe, this.body.getPosition()));
				//on prévient les obstacles uniquement lorsqu'on bouge
				//plus besoin de prevenir ! 
				//broadcastMessage(Obstacle.class, new RepulsiveMessage(id, this.body.getPosition()));
				
					
				return State.WHERE_SOULD_I_GO;
			case WHERE_SOULD_I_GO:
				if(polaire) {
					// Recuperons les derniers mouvements en memoire
					{
						SendMoveObstacleSignal sig;
						sig = this.privateMoveSignals.getFirstAvailableSignal();
						
						while (sig != null) {

							int newPosition = sig.getPosition();
							this.currentPosition = newPosition;
							this.lastObstaclePosition = sig.getLastObstaclePosition();
							//this.limit = sig.getLimit();
							sig = this.privateMoveSignals.getFirstAvailableSignal();
						}
					}
				}
				for (Message msg : getMailbox())
					sendMessage(Decisive.class, msg.getSender(), new RepulsiveMessage(0, this.groupe,
							this.body.getPosition()));
				
				return State.MOVING;
			case MOVING:
				// C'est une autre fonction qui va s'occuper du déplacement :
				return move();
			
	
		}
		return State.WHERE_SOULD_I_GO;
	}
	
	private State move() {
		State status = State.WHERE_SOULD_I_GO;
		if(polaire) {
			// passage en coordonnes cartsiennes
			dist = (this.currentPosition * Math.min(Config.PANEL_SIZE * 2, Config.PANEL_SIZE)) / 8000;
			coord.x = dist * Math.cos(angle);
			coord.x += Config.PANEL_SIZE;
			coord.y = dist * Math.sin(angle);
		
			// passage dans le bon repere
			coord.y = Config.PANEL_SIZE - coord.y;
				
			//Si on a une rotation à appliquer c'est le moment :
			if(this.rotation != 0) {
				
				//coord.x = coord.x * Math.cos(this.rotation) - coord.y * Math.sin(this.rotation);
				//coord.y = coord.x * Math.sin(this.rotation) + coord.y * Math.cos(this.rotation);
				coord.y = -coord.y + Config.PANEL_SIZE;
			}
			
			//si le mouvement change
			if(coord.x != body.getPosition().x || coord.y != body.getPosition().y) {
				//on demande la suppresion de l'ancienne position
				broadcastMessage(Decisive.class, new RepulsiveMessage(this.id, this.groupe, this.body.getPosition(),false));
				
				//on actualise la position du body
				this.body.setPosition(new Vector2d(coord));
				
				//on signale la nouvelle position
				status = State.I_M_HERE;
				
				//on signale le changement à l'agent, pour actualiser
				//la GUI
				fireSignal(new SendMoveSignal(this));
				
				if(Config.FILL_OBSTACLE_HOLE){
					fillHole();
				}
				
			}
			
		} else {
			
		//	fireSignal(new SendMoveSignal(this));
		}
		
		return status;
	}

	/**
	 * Creates RepulsiveAgent between two ObstacleAgent if they are not close enough
	 * Call when the agend move 
	 */
	private void fillHole() {
		//first, removing previous repulsiveAgent
		fireSignal(new SendDeleteRepulsiveSignal(this, this.id));
		
		
		//then checked if we need to fillholes
		if(this.lastObstaclePosition == -2){
			//no need
			this.lastObstaclePosition = -1;
		}else if(this.lastObstaclePosition != -1){
		
			//Where is the agent i need to connect with repulsiveagent
			Vector2d positionAgentPrecedent = getDistanceFromObstacle(this.lastObstaclePosition);
			
			Vector2d distance = new Vector2d(positionAgentPrecedent);
			distance.sub(this.body.getPosition());
			
			//System.out.println(this.body.getPosition()+"     "+positionAgentPrecedent+"     "+distance.length());
			Vector2d positionBoucheTrou = new Vector2d(this.body.getPosition());
			
			
			int decalageX = (int) -(distance.x*25/distance.length());
			int decalageY = (int) -(distance.y*25/distance.length());
			
			Vector2d decalage = new Vector2d(decalageX, decalageY);
			//creating new agents
			while(distance.length() > 25) {
				//System.out.println(distance.length());
				positionBoucheTrou.sub(decalage);
				fireSignal(new SendCreateObstacleSignal(this, positionBoucheTrou));
				distance = new Vector2d(positionBoucheTrou);
				distance.sub(positionAgentPrecedent);
				
			}
			
			this.lastObstaclePosition = -1;
		}

		
	}

	private Vector2d getDistanceFromObstacle(int lastObstaclePosition) {
		Vector2d pos = new Vector2d(0,0);
		
		double distance = (lastObstaclePosition * Math.min(Config.PANEL_SIZE * 2, Config.PANEL_SIZE)) / 8000;
		
		double angleObstacle;
		
		if(this.angle != 0 || this.lastObstaclePosition != 0){
			angleObstacle = this.angle-(1/180);
		}else{
			angleObstacle = 0;
			distance = Config.TELEMETER_DISTANCE;
		}
		
		pos.x = distance * Math.cos(angleObstacle);
		pos.y = distance * Math.sin(angleObstacle);
		
		
		pos.x += Config.PANEL_SIZE;
		// passage dans le bon repere
		pos.y = Config.PANEL_SIZE - pos.y;
		

		return pos;
	}

	private Vector2d getObstacleBorderPositionAt(double angleObstacle) {
		Vector2d pos = new Vector2d(0,0);
		
		double distance;
		
		distance = (8000 * Math.min(Config.PANEL_SIZE * 2, Config.PANEL_SIZE)) / 8000;
		
		pos.x = distance * Math.cos(angleObstacle);
		pos.y = distance * Math.sin(angleObstacle);
		
		
		pos.x += Config.PANEL_SIZE;
		// passage dans le bon repere
		pos.y = Config.PANEL_SIZE - pos.y;
		

		return pos;
	}

	/**
	 * Enumeration des stades de vie de cet agent. Dans l'ordre : I_M_HERE, WHERE_SOULD_I_GO et MOVING
	 * @author Etienne Sainton
	 * @since 0.1
	 */
	public enum State {
		/**
		*Donne la position de l'obstacle
		*/
		I_M_HERE,
		/**
		 * Recupere la position des agents Obstacles d'après
		 * le telemtre
		 */
		WHERE_SOULD_I_GO, 
		/**
		 * Deplacement des agents
		 */
		MOVING, 
		;
	}

}